# -*- coding: utf-8 -*-
"""
Gitee 同步模块 —— 唯一入口：GitSyncManager 类
"""
from __future__ import annotations
import json
import os
import shutil
import subprocess
import time
import datetime
from pathlib import Path
from typing import Optional, Tuple

from utils.constants import (
    GIT_REPO_URL, GIT_ACCESS_TOKEN, GIT_BRANCH,
    GIT_USER_NAME, GIT_USER_EMAIL, GIT_TIMEOUT,
    GIT_SYNC_INTERVAL, GIT_PULL_REBASE,
)


class GitSyncManager:
    """Git 同步管理器 —— 所有配置/操作必须通过本类完成"""

    def __init__(self, data_dir: str, user_id: str) -> None:
        self._data_dir = Path(data_dir)
        self._user_id = user_id
        self._repo_dir = self._data_dir / "timetracker-repo"
        self._config_file = self._data_dir / "git_config.json"

        # 配置项（私有）
        self._repo_url: str = ""
        self._access_token: str = ""
        self._branch: str = GIT_BRANCH
        self._sync_enabled: bool = False

        # 运行时
        self._last_sync_time: float = 0
        self._git_executable: str = self._find_git()

        # 初始化
        self._load_config()

    # ------------ 只读属性 ------------
    @property
    def enabled(self) -> bool:
        return self._sync_enabled

    @property
    def repo_url(self) -> str:
        return self._repo_url

    @property
    def repo_dir(self) -> Path:
        return self._repo_dir

    @property
    def last_sync(self) -> float:
        return self._last_sync_time

    # ------------ 公有 API ------------
    def setup(self, url: str, token: str) -> bool:
        """一次性配置仓库地址+令牌"""
        if not url or not token:
            raise ValueError("仓库 URL 与令牌不能为空")
        if not url.endswith(".git"):
            url += ".git"

        self._repo_url = url
        self._access_token = token
        self._sync_enabled = True
        self._save_config()
        ok: bool = self._init_repo()
        if not ok:
            self._sync_enabled = False
        return ok

    def disable(self) -> None:
        """关闭同步"""
        self._sync_enabled = False
        self._save_config()

    def sync(self) -> bool:
        """完整同步：拉+推（管理员仅拉）"""
        if not self._sync_enabled:
            print("[GIT] 同步未启用")
            return False
        if self._user_id.lower().startswith("admin"):
            return self._pull_only()

        if not self._pull():
            print("[GIT] 拉取失败，仍尝试推送")
        if not self._copy_today_csv():
            print("[GIT] 今日无数据")
            return True
        ok = self._commit_and_push()
        if ok:
            self._last_sync_time = time.time()
        return ok

    def pull_only(self) -> bool:
        """仅拉取（管理员用）"""
        print("[GIT] 管理员拉取...")
        return self._pull()     

    def ready(self) -> Tuple[bool, str]:
        """返回 (是否就绪, 原因)"""
        if not self._sync_enabled:
            return False, "同步未启用"
        if not self._git_executable:
            return False, "Git 不可用"
        if not self._repo_dir.exists():
            return False, "仓库目录不存在"
        if not (self._repo_dir / ".git").exists():
            return False, "Git 仓库未初始化"
        return True, "就绪"

    def _find_git(self) -> str:
        """找系统 Git，找不到返回 git"""
        try:
            resp = subprocess.run(
                ["where", "git"], capture_output=True, text=True, timeout=5
            )
            if resp.returncode == 0 and resp.stdout.strip():
                return resp.stdout.splitlines()[0].strip()
        except Exception:
            pass
        for p in (
            r"C:\Program Files\Git\bin\git.exe",
            r"C:\Program Files (x86)\Git\bin\git.exe",
        ):
            if os.path.isfile(p):
                return p
        return "git"

    def _load_config(self) -> None:
        if not self._config_file.exists():
            self._set_default_config()
            return
        try:
            with open(self._config_file, "r", encoding="utf-8") as f:
                cfg = json.load(f)
            self._repo_url = cfg.get("repo_url", "")
            self._access_token = cfg.get("access_token", "")
            self._branch = cfg.get("branch", GIT_BRANCH)
            self._sync_enabled = bool(self._repo_url and self._access_token)
        except Exception as e:
            print(f"[GIT] 加载配置失败: {e}")
            self._set_default_config()

    def _save_config(self) -> bool:
        try:
            cfg = {
                "repo_url": self._repo_url,
                "access_token": self._access_token,
                "branch": self._branch,
                "last_update": datetime.datetime.now().isoformat(),
            }
            with open(self._config_file, "w", encoding="utf-8") as f:
                json.dump(cfg, f, ensure_ascii=False, indent=2)
            return True
        except Exception as e:
            print(f"[GIT] 保存配置失败: {e}")
            return False

    def _set_default_config(self) -> None:
        self._repo_url = GIT_REPO_URL
        self._access_token = GIT_ACCESS_TOKEN
        self._branch = GIT_BRANCH
        self._sync_enabled = True
        self._save_config()

    def _get_auth_url(self) -> str:
        if not self._repo_url:
            return ""
        if self._repo_url.startswith("https://gitee.com/"):
            repo_path = self._repo_url.replace("https://gitee.com/", "")
            return f"https://oauth2:{self._access_token}@gitee.com/{repo_path}"
        return self._repo_url

    def _init_repo(self) -> bool:
        if self._repo_dir.exists() and not (self._repo_dir / ".git").exists():
            shutil.rmtree(self._repo_dir)
        if not self._repo_dir.exists():
            print(f"[GIT] 克隆仓库: {self._repo_url}")
            cmd = [
                self._git_executable,
                "clone",
                self._get_auth_url(),
                str(self._repo_dir),
            ]
            ok = self._exec(cmd, timeout=180)
            return ok and self._exec(
                [self._git_executable, "config", "--global", "pull.rebase", "false"]
            )
        else:
            print("[GIT] 仓库已存在，拉取更新")
            return self._exec(
                [self._git_executable, "-C", str(self._repo_dir), "pull", "origin", self._branch],
                timeout=GIT_TIMEOUT,
            )

    def _pull(self) -> bool: #普通用户拉 + 拷贝 + 提交 + 推
        return self._exec(
            [self._git_executable, "-C", str(self._repo_dir), "pull", "origin", self._branch],
            timeout=GIT_TIMEOUT,
        )

    def _commit_and_push(self) -> bool:
        os.chdir(self._repo_dir)
        self._exec([self._git_executable, "add", "."])
        # 无变更直接成功
        status = self._exec([self._git_executable, "status", "--porcelain"], capture_out=True)
        if not status or not status.stdout.strip():
            print("[GIT] 无更改需要提交")
            return True
        msg = f"Update: {self._user_id} {datetime.datetime.now():%Y-%m-%d %H:%M}"
        ok = self._exec([self._git_executable, "commit", "-m", msg])
        if not ok:
            return False
        return self._exec(
            [self._git_executable, "push", "origin", self._branch],
            timeout=120,
        )

    def _copy_today_csv(self) -> bool:
        today = datetime.date.today().strftime("%Y-%m-%d")
        src = self._data_dir / "logs" / f"{today}_{self._user_id}.csv"
        if not src.exists():
            return False
        month = today[:7]
        dst_dir = self._repo_dir / "data" / f"site-{self._site_prefix()}" / self._user_id / month
        dst_dir.mkdir(parents=True, exist_ok=True)
        dst = dst_dir / f"{today}.csv"
        shutil.copy2(src, dst)
        print(f"[GIT] 已复制数据文件: {dst}")
        return True

    def _site_prefix(self) -> str:
        uid = self._user_id
        if uid.startswith("BZ"):
            return "A"
        if uid.startswith("SH"):
            return "B"
        return "other"

    def _exec(
        self,
        cmd: list[str],
        *,
        timeout: int = GIT_TIMEOUT,
        capture_out: bool = False,
    ) -> subprocess.CompletedProcess | None:
        try:
            result = subprocess.run(
                cmd,
                capture_output=capture_out,
                text=True,
                encoding="utf-8",
                timeout=timeout,
                creationflags=subprocess.CREATE_NO_WINDOW,
            )
            if result.returncode != 0:
                print(f"[GIT] 命令失败: {' '.join(cmd)} -> {result.stderr.strip()}")
            return result
        except subprocess.TimeoutExpired:
            print("[GIT] 命令超时")
            return None
        except Exception as e:
            print(f"[GIT] 命令异常: {e}")
            return None